<template>
  <el-input
    :value="selectName"
    class="org-select"
    v-bind="$attrs"
    @click="openDialog"
  />

  <el-dialog
    title="请选择单位"
    v-model="dialogVisible"
    width="950px"
    destroy-on-close
    @close="closeDialog"
  >
    <div class="org-select-dialog">
      <header class="header">
        <el-form
          :inline="true"
          :model="query"
          size="mini"
          label-width="80px"
        >
          <el-form-item label="查询名称">
            <el-input
              v-model="query.name"
              @keyup.enter="getOrgMap"
            />
          </el-form-item>
          <el-form-item>
            <el-button
              type="primary"
              @click="getOrgMap"
            >
              搜索
            </el-button>
          </el-form-item>

          <el-form-item v-if="!$props.radioMode">
            <el-button @click="selectAll">
              全选
            </el-button>

            <el-button @click="selected = []">
              清除
            </el-button>

            <el-button @click="selectReverse">
              反选
            </el-button>
          </el-form-item>
        </el-form>
      </header>

      <main
        class="optional"
        v-loading="loading"
      >
        <el-scrollbar>
          <component
            :is="selectComponent"
            v-model="selected"
          >
            <ul>
              <li
                v-for="(item, key) in orgMap"
                :key="key"
              >
                <header>
                  <span class="text">{{item.name}}</span>
                  <el-button
                    v-if="!$props.radioMode"
                    type="text"
                    @click="selectGroup(item.orgList)"
                  >
                    全选
                  </el-button>
                  <el-button
                    v-if="!$props.radioMode"
                    type="text"
                    @click="clearGroup(item.orgList)"
                  >
                    清除
                  </el-button>
                </header>
                <div class="options">
                  <component
                    :is="selectComponentItem"
                    v-for="check in item.orgList"
                    :key="check[finalProps.id]"
                    :label="check[finalProps.id]"
                  >
                    {{check.name}}
                  </component>
                </div>
              </li>
            </ul>
          </component>
        </el-scrollbar>
      </main>

      <aside class="selected">
        <header>
          已选择
        </header>
        <el-scrollbar>
          <el-tree
            :data="selectedObj"
            :props="finalProps"
          >
            <template #default="{ data, node }">
              <span class="custom-tree-node">
                <span>{{ node.label }}</span>

                <el-button
                  type="text"
                  @click="removeItem(data)"
                >
                  <i class="el-icon-close" />
                </el-button>
              </span>
            </template>
          </el-tree>
        </el-scrollbar>
      </aside>
    </div>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="closeDialog">取 消</el-button>
        <el-button
          type="primary"
          @click="submit"
        >
          确 定
        </el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script>
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
import { getOrgMap as getOrgMapAPi } from '@/api/system'
import { useBaseDialog } from '@/hooks'

const PROPS = {
  id: 'id',
  label: 'name'
}

export default {
  name: 'OrgSelect',
  props: {
    select: {
      // 已选择数据
      type: [String, Array], // 默认Array, 单选模式下为String
      default: null
    },

    props: {
      // 键值对应
      type: Object,
      default: () => {
        return PROPS
      }
    },

    radioMode: {
      // 单选模式
      type: Boolean,
      default: false
    }
  },
  emits: ['update:select', 'update-names'],
  setup(props, { emit }) {
    const loading = ref(false)
    const { dialogVisible, open: openDialog, close } = useBaseDialog()

    // 动态组件 根据模式 使用单选或多选
    const selectComponent = computed(() => {
      return props.radioMode ? 'el-radio-group' : 'el-checkbox-group'
    })
    // 同上 这个是内部子组件
    const selectComponentItem = computed(() => {
      return props.radioMode ? 'el-radio' : 'el-checkbox'
    })

    const query = reactive({
      // 查询参数
      name: ''
    })
    const orgMap = ref([]) // 组织列表
    const orgMapBackup = ref({}) // 列表备份
    const orgFlat = computed(() => {
      // 组织树铺平到一维数组
      return orgMapBackup.value.map((item) => item.orgList).flat()
    })
    const selected = ref(props.radioMode ? '' : []) // 已选择节点（id）
    const selectName = ref('') // 已选择节点的展示名称
    const backupSelected = ref([]) // 已选择节点备份

    // 已选择节点原始对象
    const selectedObj = computed(() => {
      // 单选模式
      if (props.radioMode) {
        const target = orgFlat.value.find((item) => item[finalProps.value.id] === selected.value)
        return target ? [target] : []
      }
      // 多选模式
      return orgFlat.value.filter((item) => {
        const target = selected.value.find((el) => el === item[finalProps.value.id])
        return target
      })
    })

    const finalProps = computed(() => {
      // 键值对应关系
      return {
        // 自定义数据与默认数据合并
        ...PROPS,
        ...props.props
      }
    })

    // 确认提交
    const submit = () => {
      if (props.radioMode) {
        // 单选模式
        selectName.value = selectedObj.value[0][finalProps.value.label]
        emit('update-names', selectName.value)
        emit('update:select', selected.value)
        close()
        return
      }
      // 多选模式
      const ids = []
      const names = []
      selectedObj.value.forEach((item) => {
        ids.push(item[finalProps.value.id])
        names.push(item[finalProps.value.label])
      })
      selectName.value = names.join(',')
      emit('update:select', ids)
      emit('update-names', selectName.value)
      close()
    }

    // 取消并关闭
    const closeDialog = () => {
      close()
      selected.value = JSON.parse(JSON.stringify(backupSelected.value))
    }

    // 获取机构数据
    const getOrgMap = async () => {
      loading.value = true
      const res = await getOrgMapAPi(query)
      orgMap.value = res.data
      loading.value = false
    }

    /**
     * 填充默认选中的名称
     * 确保只在orgMap、selected都初始化后才执行
     */
    const setSelectName = () => {
      if (orgMap.value.length > 0) {
        if (props.radioMode && selected.value !== null && selected.value !== '') {
          // 单选模式 值不为空
          selectName.value = orgFlat.value.find((el) => el[finalProps.value.id] === selected.value)[
            finalProps.value.label
          ]
        } else if (selected.value instanceof Array && selected.value.length > 0) {
          // 多选模式 数组长度不为0
          selectName.value = selectedObj.value.map((item) => item[finalProps.value.label]).join(',')
        }
      }
    }

    // 外部select数据变化时 勾选默认数据
    watch(
      () => props.select,
      () => {
        // 根据模式判断赋值方式
        selected.value = props.radioMode ? props.select : [...props.select]
        backupSelected.value = JSON.parse(JSON.stringify(selected.value))
        setSelectName()
      },
      { immediate: true }
    )

    // 初始化
    onMounted(async () => {
      query.name = ''
      await getOrgMap() // 获取机构数据
      orgMapBackup.value = orgMap.value // 备份数据
      setSelectName()
    })

    // 全选
    const selectAll = () => {
      selected.value = orgMap.value
        .map((item) => item.orgList)
        .flat()
        .map((item) => item[finalProps.value.id])
    }

    // 反选
    const selectReverse = () => {
      const oldSelect = [...selected.value]
      selected.value = orgFlat.value
        .filter((item) => {
          return !oldSelect.find((el) => el === item[finalProps.value.id])
        })
        .map((item) => item[finalProps.value.id])
    }

    // 删除某一项
    const removeItem = (data) => {
      if (props.radioMode) {
        selected.value = ''
      } else {
        selected.value = selected.value.filter((item) => item !== data[finalProps.value.id])
      }
    }

    // 全选某个组
    const selectGroup = (arr) => {
      const ids = arr.map((item) => item[finalProps.value.id])
      selected.value = [...selected.value, ...ids]
    }

    // 清空某个组
    const clearGroup = (arr) => {
      arr.forEach((item) => {
        const i = selected.value.findIndex((el) => el === item[finalProps.value.id])
        if (i !== -1) {
          selected.value.splice(i, 1)
        }
      })
    }

    return {
      loading,
      selectComponent,
      selectComponentItem,
      query,
      selected,
      selectName,
      finalProps,
      dialogVisible,
      openDialog,
      closeDialog,
      submit,
      orgMap,
      getOrgMap,
      selectedObj,
      selectAll,
      selectReverse,
      removeItem,
      selectGroup,
      clearGroup
    }
  }
}
</script>

<style lang="scss" scoped>
.org-select-dialog {
  display: grid;
  grid-template-columns: auto 200px;
  grid-template-rows: 40px 50vh;
  grid-gap: 20px;
  grid-template-areas:
    'header header'
    'optional selected';

  .header {
    grid-area: header;
    display: flex;
  }

  .optional {
    grid-area: optional;
    border: 1px solid #dcdfe6;

    :deep(.el-scrollbar__view) {
      padding: 0;

      .el-radio-group {
        width: 100%;
      }
    }

    ul {
      margin: 0;
      padding: 0;
      list-style-type: none;

      li {
        header {
          padding: 0 20px;
          display: flex;
          align-items: center;
          background: #dcdfe6;
          overflow: auto;
          font-size: 14px;
          line-height: 30px;

          .text {
            width: 200px;
          }
        }

        .options {
          padding: 0 20px;
          display: flex;
          flex-wrap: wrap;
          overflow: hidden;

          .el-checkbox,
          .el-radio {
            margin-right: 0;
            width: 25%;
            height: 40px;
            line-height: 40px;
            overflow: hidden;
          }
        }
      }
    }
  }

  .selected {
    grid-area: selected;
    border: 1px solid #dcdfe6;

    header {
      padding: 0 20px;
      height: 30px;
      line-height: 30px;
      background: #dcdfe6;
    }

    :deep(.el-scrollbar__view) {
      padding: 0;
    }

    .custom-tree-node {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-size: 14px;
      padding-right: 8px;
    }
  }
}
</style>